#include "script.h"
#include <stdexcept>

namespace SDHuaYi_Script
{
    // 注意：此为全局变量
    QString updataScriptFilePath;     // 脚本文件路径
    QString updataFirmwareFilePath;   // cos固件路径
    QString updataAlgLibFilePath;     // 算法库路径
    QString updataUserAlgLibFilePath; // 客户自定义算法库路径
    QString updataNewUid;             // 新的UID
    QString updataOldUid;             // 旧的UID
    script::script()
    {
    }

    void script::SetCurrentPCByIndex(qint32 curIndex)
    {
        for (qint32 i = 0; i < scriptCmds.count(); i++)
        {
            if ((scriptCmds[i].startIndex <= curIndex) && (scriptCmds[i + 1].startIndex > curIndex))
            {
                // 返回当前命令
                PC = i;
            }
        }
    }

    command script::GetCurrentCmdByIndex(qint32 curIndex)
    {
        for (qint32 i = 0; i < scriptCmds.count() - 1; i++)
        {
            // 判断位置
            if ((scriptCmds[i].startIndex <= curIndex) && (curIndex <= (scriptCmds[i].startIndex + scriptCmds[i].Value.count())))
            {
                // 返回当前命令
                return scriptCmds[i];
            }
        }
        return command();
    }

    void script::ScriptSetPC(qint32 iPC)
    {
        PC = iPC;
    }

    void script::ScriptBreak()
    {
        // 设置当前PC
        PC = scriptCmds.count();
    }

    QString script::GetScript(QString ScriptText, QString ScriptPath)
    {
        scriptCmds.clear();
        Labels.clear();

        ScriptFile scriptFile = ScriptFile(this->scriptCmds, this->Labels);

        // 展开脚本
        scriptFile.ScriptSpread(ScriptText, ScriptPath);

        scriptCmds.append(scriptFile.ScriptCmds);

        for (qint32 i = 0; i < scriptFile.ScriptCmds.count() - 1; i++)
        {
            strScript += scriptFile.ScriptCmds[i].cmdStr + "\n";
        }
        strScript += scriptFile.ScriptCmds[scriptFile.ScriptCmds.count() - 1].cmdStr;
        return strScript;
    }

    void script::GetScript(QString scriptFileName)
    {
        scriptCmds.clear();
        Labels.clear();

        ScriptFile scriptFile = ScriptFile(this->scriptCmds, this->Labels);

        scriptFile.ScriptSpread(scriptFileName);

        scriptCmds.append(scriptFile.ScriptCmds);
    }

    void script::PUSH(qint32 PC)
    {
        for (auto pc : STACK)
        {
            if (PC == pc)
            {
                throw std::runtime_error("不支持递归调用");
            }
        }
        STACK.append(PC);
    }

    qint32 script::POP()
    {
        if (STACK.count() == 0)
        {
            throw std::runtime_error("不完整的函数调用");
        }

        // 最后一个元素
        qint32 PC = STACK[STACK.count() - 1];
        STACK.removeAt(STACK.count() - 1);

        return PC;
    }

    qint32 script::GetPC(QString label)
    {
        for (auto pcLabel : Labels)
        {
            if (pcLabel.Name == label)
            {
                return pcLabel.LineNum;
            }
        }
        throw std::runtime_error(QString("未定义的标号 %1").arg(label).toStdString());
    }

    QString script::RunCmd(cardReaderDev reader, QString strCmd, EnumCmdResultType &cmdResultType)
    {
        QString res = QString();
        cmdResultType = CMD_RESULT_NONE;
        ScriptFile scriptFile = ScriptFile(this->scriptCmds, this->Labels);
        command cmd = scriptFile.getCommand(strCmd);
        switch (cmd.Type)
        {
        case CMD_APDU:
            res = Apdu_Process(cmd, reader, cmdResultType);
            break;
        case CMD_DATAOP:
            res = DataOp_Process(cmd);
            break;
        case CMD_INSERT:
            res = Insert_Process(cmd, reader);
            break;
        default:
            res = strCmd;
            break;
        }
        return res;
    }

    bool script::JudgeProcess(command &currentCmd)
    {
        try
        {
            if (currentCmd.Excute(currentCmd.Value, paras) == "true")
            {
                return true;
            }
            return false;
        }
        catch (...)
        {
            throw std::runtime_error("Command State Error!");
        }
    }

    QString script::DataOp_Process(command &currentCmd)
    {
        try
        {
            QString result = currentCmd.Excute(currentCmd.Value, paras, hY_RSA_PUBLIC_KEY);
            if (!result.isEmpty())
            {
                result += "\n";
            }
            return result;
        }
        catch (...)
        {
            throw std::runtime_error("Command State Error!");
        }
    }

    QString script::Insert_Process(command &currentCmd, cardReaderDev reader)
    {
        QString result = QString();
        try
        {
            currentCmd.Excute(reader, paras);
            // 花费时间
            result += "Spent Time: " + QString::number(reader.SpendTime) + "ms\n";
            result += "ATR: " + currentCmd.Response + "\n";

            return result;
        }
        catch (...)
        {
            // 上电无返回
            // 花费时间
            result += "Spent Time: " + QString::number(reader.SpendTime) + "ms\n";
            result += "Insert Error: Device not found--\n";

            throw std::runtime_error(QString("%1").arg(result).toStdString());
        }
    }

    QString script::Apdu_Process(command &currentCmd, cardReaderDev reader, EnumCmdResultType &ResultType)
    {
        QString result = QString();
        ResultType = CMD_RESULT_NONE;
        QString realCmd = QString();

        try
        {
            if (reader.readerType == READER_HY3M3)
            {
                // TODO:暂不支持此读卡器
            }
            else
            {
                currentCmd.Excute(reader, paras, hY_RSA_PUBLIC_KEY);
            }
        }
        catch (...)
        {
            // TODO:错误处理较为繁琐，暂不添加
            throw std::runtime_error("错误");
        }
        // 花费时间
        result += "Spent Time: " + QString::number(reader.SpendTime) + "ms\n";
        //
        result += currentCmd.Response + " : " + currentCmd.SW + "\n";

        // 检查脚本模式
        if (reader.readerType == READER_HY3M3)
        {
            // TODO:暂不支持
        }
        else
        {
            // 设置状态
            if (!currentCmd.ExptRe.isEmpty())
            {
                if (currentCmd.ExptRe != currentCmd.SW.replace(" ", ""))
                {
                    // 失败
                    ResultType = CMD_RESULT_FAIL;
                    throw std::runtime_error(QString("%1").arg(result).toStdString());
                }
                else
                {
                    // 成功
                    ResultType = CMD_RESULT_PASS;
                }
            }
            else
            {
                // 警告
                if ((currentCmd.SW.mid(0, 2) != "90") && (currentCmd.SW.mid(0, 2) != "61"))
                {
                    // 提示
                    ResultType = CMD_RESULT_WARNING;
                    throw std::runtime_error(QString("%1").arg(result).toStdString());
                }
            }
        }
        return result;
    }

    bool script::ExcuteCMD(command &currentCmd, cardReaderDev reader)
    {
        currentCmd.cmdResult.ResultType = CMD_RESULT_NONE;
        currentCmd.cmdResult.ResultMessage = "Line " + QString::number(PC) + ": " + currentCmd.Value + currentCmd.Comment + "\n";

        PauseFlag = false;
        ExitFlag = false;
        switch (currentCmd.Type)
        {
        case CMD_JUDGEMENT:
        {
            // 判断语句
            return JudgeProcess(currentCmd);
        }
        case CMD_DATAOP:
        {
            // 数据处理
            currentCmd.cmdResult.ResultType = CMD_RESULT_NONE;
            currentCmd.cmdResult.ResultMessage += DataOp_Process(currentCmd);
        }
        break;
        case CMD_INSERT:
        {
            try
            {
                currentCmd.cmdResult.ResultMessage += Insert_Process(currentCmd, reader);
            }
            catch (...)
            {
                try
                {
                    currentCmd.cmdResult.ResultMessage += Insert_Process(currentCmd, reader);
                }
                catch (...)
                {
                    try
                    {
                        currentCmd.cmdResult.ResultMessage += Insert_Process(currentCmd, reader);
                    }
                    catch (std::runtime_error &ex)
                    {
                        // 无返回
                        // 触发返回数据
                        currentCmd.cmdResult.ResultMessage += ex.what();
                        currentCmd.cmdResult.ResultType = CMD_RESULT_FAIL;

                        PC++;
                        return false;
                    }
                }
            }
        }
        break;
        case CMD_APDU:
        {
            EnumCmdResultType ResultType = CMD_RESULT_NONE;

            try
            {
                currentCmd.cmdResult.ResultMessage += Apdu_Process(currentCmd, reader, ResultType);
                currentCmd.cmdResult.ResultType = ResultType;
            }
            catch (std::runtime_error &ex)
            {
                // 执行无返回或与Judge不同
                // 报错退出
                currentCmd.cmdResult.ResultType = ResultType;
                currentCmd.cmdResult.ResultMessage += ex.what();

                if (ResultType == CMD_FORMAT_ERR)
                {
                    // 脚本格式错误
                    // PC 保留在当前行供修正
                    return false;
                }

                if (ResultType != CMD_RESULT_WARNING)
                {
                    // 触发返回数据
                    PC++;
                    return false;
                }
            }
        }
        break;
        case CMD_EMPTY:
        {
            currentCmd.cmdResult.ResultMessage = "\n";
        }
        break;
        case CMD_COMMENT:
        case CMD_LABEL:
        {
            // 注释 定义
        }
        break;
        case CMD_DEFINE:
        {
            BaseOP BOP = BaseOP();
            QString sDValue = "";
            QString sCMD = "";
            // 注释 定义 ，变量初始化
            if (currentCmd.Value.toLower().startsWith("string"))
            {
                sCMD = currentCmd.Value.replace("string ", "");
                sDValue = "";
            }
            else
            {
                sCMD = currentCmd.Value.replace("integer ", "");
                sDValue = "0";
            }

            QStringList cmdParas = sCMD.split(QRegExp(","), QString::SkipEmptyParts);
            for (int m = 0; m < cmdParas.count(); m++)
            {
                // 添加到变量表中
                Para para1 = Para(cmdParas[m], sDValue);
                BOP.ParaInsert(paras, para1);
            }
        }
        break;
        case CMD_FLOW:
        {
            // 取cmd值
            QStringList cmdParas = currentCmd.Value.split(' ', QString::SkipEmptyParts);
            QString scmdValue = cmdParas[0];
            if (scmdValue.contains("F_FlagErr")) // F_FlagErr特殊处理
            {
                scmdValue = "F_FlagErr";
            }

            if (scmdValue == "pause")
            {
                PauseFlag = true;
                PC++;
                return true;
            }
            else if (scmdValue == "exit")
            {
                ExitFlag = true;
                return false;
            }
            else if (scmdValue == "F_FlagErr")
            {
                // 报错
                currentCmd.cmdResult.ResultType = CMD_RESULT_FAIL;
                currentCmd.cmdResult.ResultMessage = currentCmd.Value.mid(currentCmd.Value.indexOf('(') + 1,
                                                                          currentCmd.Value.indexOf(")") - currentCmd.Value.indexOf('(') - 1) +
                                                     "\n";
                PC++;
                return true;
            }
            else if (scmdValue == "if")
            {
                // 处理if
                if (!currentCmd.Value.contains(" then "))
                {
                    throw std::runtime_error(QString("Line %1语法错误，没有对应的操作").arg(PC).toStdString());
                }
                // 检查条件是否满足
                // 获取条件

                QString temp = currentCmd.Value.replace("then", "$");
                QString judge = temp.mid(2, temp.indexOf('$') - 2).trimmed();

                command judgeCmd = command(judge, CMD_JUDGEMENT);

                if (false == ExcuteCMD(judgeCmd))
                {
                    // 下一条命令
                    PC++;
                    return true;
                }

                // 后续操作
                command nextOp = command(temp.mid(temp.indexOf('$') + 1).trimmed(), CMD_FLOW);

                ExcuteCMD(nextOp);

                currentCmd.cmdResult.ResultMessage += nextOp.cmdResult.ResultMessage;
                currentCmd.cmdResult.ResultType = nextOp.cmdResult.ResultType;

                // 有可能操作了PC
                return true;
            }
            else if (scmdValue == "goto")
            {
                if (cmdParas.count() != 2)
                {
                    throw std::runtime_error(QString("Line %1语法错误 存在不能识别的参数").arg(PC).toStdString());
                }
                // goto
                PC = GetPC(cmdParas[1]);
                // 执行下一条指令
                return true;
            }
            else if (scmdValue == "call")
            {
                if (cmdParas.count() != 2)
                {
                    throw std::runtime_error(QString("Line %1语法错误 存在不能识别的参数").arg(PC).toStdString());
                }

                PUSH(PC + 1);

                // goto
                PC = GetPC(cmdParas[1]);

                return true;
            }
            else if (scmdValue == "ret")
            {
                // 函数返回
                PC = POP();
                return true;
            }
            else
            {
                throw std::runtime_error(QString("Line %1语法错误 不识别的命令").arg(PC).toStdString());
            }
        }
        }

        // Next Cmd
        PC++;
        slog += currentCmd.sLOG;
        return true;
    }

    bool script::Run(cardReaderDev reader, bool ErrStopFlag)
    {
        while (PC < scriptCmds.count())
        {
            // 设置当前PC
            int process = PC * 100 / scriptCmds.count();
            reader.piReader->update_percent(process);

            if (PC == scriptCmds.count())
            {
                // 结束执行
                PC = 0;
                break;
            }

            command &currentCmd = scriptCmds[PC];

            try
            {
                bool res = ExcuteCMD(currentCmd, reader);

                if ((false == res) && (ErrStopFlag == true))
                {
                    return false;
                }
            }
            catch (std::runtime_error &ex)
            {
                throw std::runtime_error(ex.what());
                //                return false;
            }
        }

        // 执行结束
        int process = PC * 100 / scriptCmds.count();
        reader.piReader->update_percent(process);
        return true;
    }

}
